﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Configuration;
using System.Web;
using System.Configuration;

namespace SecureCookie.Implementation.Hook.Impl
{
    /// <summary>
    /// Default implemented <see cref="IServerSideDataProvider"/> that uses
    /// "system.web/machineKey" as secret validation key, the configured lifetime for session based cookies
    /// and either the public key (if client-server SSL) or the client ip address as unique client value.
    /// </summary>
    class DefaultServerSideDataProvider : IServerSideDataProvider
    {
        private ILogger logger;

        public DefaultServerSideDataProvider(ILogger logger)
        {
            if (logger == null) throw new ArgumentNullException("logger");
            this.logger = logger;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "HttpContext.Current is a global argument")]
        public Protocol.ServerSideData GetServerSideData()
        {
            try
            {
                if (HttpContext.Current == null) throw new ArgumentNullException("HttpContext.Current");
                var data = new Protocol.ServerSideData(

                     GetSecretKeyFromMachineValidationKey(),
                     TimeSpan.FromMinutes(SecureCookieSettings.Default.MaxLifetimeForSessionBasedCookiesInMinutes),
                 (HttpContext.Current.Request.ClientCertificate.IsPresent ?
                        Encoding.UTF8.GetString(HttpContext.Current.Request.ClientCertificate.PublicKey) :
                        HttpContext.Current.Request.UserHostAddress)
                );
                logger.LogVerbose(() => "Going to use [{0}] as MaxLifetimeForSessionBasedCookies", () => new object[] { data.MaxLifetimeForSessionBasedCookies });
                return data;
            }
            catch (Exception ex)
            {
                logger.LogException(ex);
                throw;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "It would be overhead to create a custom exception for this")]
        string GetSecretKeyFromMachineValidationKey()
        {
            try
            {
                var webSection = WebConfigurationManager.GetSection("system.web/machineKey") as MachineKeySection;
                if (string.IsNullOrEmpty(webSection.ValidationKey)) throw new ArgumentNullException("system.web/machineKey/validationKey");
                return webSection.ValidationKey;
            }
            catch (Exception ex)
            {
                logger.LogVerbose(() => "Error while reading out the ValidationKey", () => new object[] { });
                throw new ConfigurationErrorsException("system.web/machineKey/validationKey should be configured in the web.config", ex);
            }

        }
    }
}
